<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>areaDetector: EPICS software for area detectors</title>
  <meta content="text/html; charset=ISO-8859-1" http-equiv="Content-Type" />
  <style type="text/css">
    .style1
    {
      height: 27px;
    }
  </style>
</head>
<body>
  <div style="text-align: center">
    <h1>
      areaDetector: EPICS Area Detector Support</h1>
    <h2>
      Release 3-3</h2>
    <h2>
      June 26, 2018</h2>
    <h2>
      Mark Rivers</h2>
    <h2>
      University of Chicago</h2>
  </div>
  <h2>
    Contents</h2>
  <ul>
    <li><a href="#Overview">Overview</a></li>
    <li><a href="#Architecture">Architecture</a></li>
    <li><a href="#Implementation_details">Implementation details</a>
      <ul>
        <li><a href="#asynPortDriver">asynPortDriver</a></li>
        <li><a href="#NDArray">NDArray</a></li>
        <li><a href="#NDArrayPool">NDArrayPool</a></li>
        <li><a href="#NDAttribute">NDAttribute</a></li>
        <li><a href="#NDAttributeList">NDAttributeList</a></li>
        <li><a href="#PVAttribute">PVAttribute</a></li>
        <li><a href="#paramAttribute">paramAttribute</a></li>
        <li><a href="#asynNDArrayDriver">asynNDArrayDriver</a></li>
      </ul>
    </li>
    <li>Detector drivers
      <ul>
        <li><a href="#ADDriver">ADDriver</a></li>
        <li><a href="#Guidelines">Guidelines and rules for drivers</a></li>
        <li><a href="ADCSimDetectorDoc.html">ADC Simulation detector driver</a></li>
        <li><a href="adscDoc.html">ADSC driver</a></li>
        <li><a href="andorDoc.html">Andor driver (Andor CCD cameras)</a></li>
        <li><a href="andor3Doc.html">Andor3 driver (Andor sCMOS cameras)</a></li>
        <li><a href="https://github.com/areaDetector/aravisGigE/blob/master/README.md">aravisGigE
          driver</a></li>
        <li><a href="BrukerDoc.html">Bruker driver</a></li>
        <li><a href="ADCameraLinkDriver.html">CameraLink driver</a></li>
        <li><a href="DexelaDoc.html">Dexela driver</a></li>
        <li><a href="https://github.com/areaDetector/ADFastCCD/blob/master/README.md">FastCCD
          driver</a></li>
        <li><a href="http://controls.diamond.ac.uk/downloads/support/firewireDCAM/index.html">
          Firewire Linux driver</a></li>
        <li><a href="FirewireWinDoc.html">Firewire Windows driver</a></li>
        <li><a href="ADLambda.html">Lambda driver</a></li>
        <li><a href="LightFieldDoc.html">LightField driver</a></li>
        <li><a href="Mar345Doc.html">mar345 driver</a></li>
        <li><a href="MarCCDDoc.html">MarCCD driver</a></li>
        <li><a href="https://github.com/areaDetector/ADMerlin/blob/master/README.md">Merlin
          driver</a></li>
        <li><a href="https://github.com/areaDetector/ADMythen/blob/master/README.md">Mythen
          driver</a></li>
        <li><a href="NDDriverStdArraysDoc.html">NDDriverStdArrays driver</a></li>
        <li><a href="https://github.com/areaDetector/ADnED/blob/master/README.md">Neutron
          event data driver</a></li>
        <li><a href="PCODriver.html">PCO driver</a></li>
        <li><a href="PerkinElmerDoc.html">Perkin-Elmer flat panel driver</a></li>
        <li><a href="PSLDoc.html">Photonic Science Limited (PSL) driver</a></li>
        <li><a href="PhotonIIDoc.html">Bruker Photon II driver</a></li>
        <li><a href="pilatusDoc.html">Pilatus driver</a></li>
        <li><a href="PixiradDoc.html">Pixirad driver</a></li>
        <li><a href="PointGreyDoc.html">Point Grey driver</a></li>
        <li><a href="ADSpinnakerDoc.html">FLIR Spinnaker driver</a></li>
        <li><a href="prosilicaDoc.html">Prosilica driver</a></li>
        <li><a href="pvaDriverDoc.html">PVAccess (EPICS V4) driver</a></li>
        <li><a href="pvcamDoc.html">PVCAM driver</a></li>
        <li><a href="QImagingDoc.html">QImaging driver</a></li>
        <li><a href="RoperDoc.html">Roper driver</a></li>
        <li><a href="simDetectorDoc.html">Simulation detector driver</a></li>
        <li><a href="URLDriverDoc.html">URL driver</a></li>
      </ul>
    </li>
    <li><a href="pluginDoc.html">Plugins</a>
      <ul>
        <li><a href="pluginDoc.html#NDPluginDriver">NDPluginDriver</a></li>
        <li><a href="pluginDoc.html#Guidelines">Guidelines and rules for plugins</a></li>
        <li><a href="NDPluginAttPlot.html">NDPluginAttrPlot</a></li>
        <li><a href="NDPluginAttribute.html">NDPluginAttribute</a></li>
        <li><a href="NDPluginCircularBuff.html">NDPluginCircularBuff</a></li>
        <li><a href="NDPluginColorConvert.html">NDPluginColorConvert</a></li>
        <li><a href="NDPluginFFT.html">NDPluginFFT</a></li>
        <li><a href="NDPluginFile.html">NDPluginFile</a></li>
        <li><a href="NDPluginGather.html">NDPluginGather</a></li>
        <li><a href="NDPluginOverlay.html">NDPluginOverlay</a></li>
        <li><a href="NDPluginProcess.html">NDPluginProcess</a></li>
        <li><a href="NDPluginPva.html">NDPluginPva</a></li>
        <li><a href="NDPluginROI.html">NDPluginROI</a></li>
        <li><a href="NDPluginROIStat.html">NDPluginROIStat</a></li>
        <li><a href="NDPluginScatter.html">NDPluginScatter</a></li>
        <li><a href="NDPluginStats.html">NDPluginStats</a></li>
        <li><a href="NDPluginStdArrays.html">NDPluginStdArrays</a></li>
        <li><a href="NDPluginTimeSeries.html">NDPluginTimeSeries</a></li>
        <li><a href="NDPluginTransform.html">NDPluginTransform</a></li>
        <li><a href="NDPosPlugin.html">NDPosPlugin</a></li>
        <li><a href="https://github.com/areaDetector/ffmpegServer/blob/master/README.md">ffmpegServer
          plugin</a></li>
      </ul>
    </li>
    <li><a href="#MEDM_screens">MEDM screens</a></li>
    <li><a href="areaDetectorViewers.html">Viewers</a>
      <ul>
        <li><a href="areaDetectorViewers.html#ImageJViewer">ImageJ Viewer</a></li>
        <li><a href="areaDetectorViewers.html#IDLViewer">IDL Viewer</a></li>
      </ul>
    </li>
    <li><a href="#Installation">Installation, configuration, and startup</a></li>
    <li><a href="#Acknowledgments">Acknowledgments and licenses</a></li>
  </ul>
  <p>
    &nbsp;</p>
  <h2 id="Overview">
    Overview</h2>
  <p>
    The areaDetector module provides a general-purpose interface for area (2-D) detectors
    in <a href="http://www.aps.anl.gov/epics/">EPICS</a>. It is intended to be used
    with a wide variety of detectors and cameras, ranging from high frame rate CCD and
    CMOS cameras, pixel-array detectors such as the Pilatus, and large format detectors
    like the Perkin Elmer flat panels.</p>
  <p>
    The goals of this module are:
  </p>
  <ul>
    <li>Minimize the amount of code that needs to be written to implement a new detector.</li>
    <li>Provide a standard interface defining the functions and parameters that a detector
      driver must support.</li>
    <li>Provide a set of base EPICS records that will be present for every detector using
      this module. This allows the use of generic EPICS clients for displaying images
      and controlling cameras and detectors.</li>
    <li>Allow easy extensibility to take advantage of detector-specific features beyond
      the standard parameters.</li>
    <li>Have high-performance. Applications can be written to get the detector image data
      through EPICS, but an interface is also available to receive the detector data at
      a lower-level for very high performance.</li>
    <li>Provide a mechanism for device-independent real-time data analysis such as regions-of-interest
      and statistics.</li>
    <li>Provide detector drivers for commonly used detectors in synchrotron applications.
      These include GigE video cameras, IEEE 1394 (Firewire) cameras, CCD x-ray detectors,
      scientific CCD and CMOS cameras, online imaging plate detectors, pixel-array detectors,
      amorphous silicon and CMOS flat panel detectors, and many others.</li>
  </ul>
  <p>
    &nbsp;</p>
  <h2 id="Architecture">
    Architecture</h2>
  <p>
    The architecture of the areaDetector module is shown below.</p>
  <p style="text-align: center">
    <img alt="areaDetectorArchitecture.png" src="areaDetectorArchitecture.png" style="width: 1000px;
      height: 750px;" /></p>
  <p>
    From the bottom to the top this architecture consists of the following:</p>
  <ul>
    <li>Layer 1. This is the layer that allows user written code to communicate with the
      hardware. It is usually provided by the detector vendor. It may consist of a library
      or DLL, of a socket protocol to a driver, a Microsoft COM interface, etc.</li>
    <li>Layer 2. This is the driver that is written for the areaDetector application to
      control a particular detector. It is written in C++ and inherits from the ADDriver
      class. It uses the standard asyn interfaces for control and status information.
      Each time it receives a new data array it can pass it as an NDArray object to all
      Layer 3 clients that have registered for callbacks. This is the only code that needs
      to be written to implement a new detector. Existing drivers range from about 800
      to 2600 lines of code.</li>
    <li>Layer 3. Code running at this level is called a "plug-in". This code registers
      with a driver for a callback whenever there is a new data array. The existing plugins
      implement file saving (NDPluginFile), region-of-interest (ROI) calculations (NDPluginROI),
      statistics calculations (NDPluginStats, NDPluginROIStat), image processing (NDPluginProcess),
      geometric transformations (NDPluginTransform), buffering and triggering (NDPluginCircularBuff),
      color mode conversion (NDPluginColorConvert), graphics and text overlays (NDPluginOverlay),
      exporting NDArray attributes as scalar and waveform records (NDPluginAttribute),
      conversion of detector data to standard EPICS array types for use by Channel Access
      clients (NDPluginStdArrays), and conversion of NDArrays to EPICS pvAccess (NDPluginPva).
      Plugins are written in C++ and inherit from NDPluginDriver. Existing plugins range
      from about 300 to 3000 lines of code.</li>
    <li>Layer 4. This is standard asyn device support that comes with the EPICS asyn module.</li>
    <li>Layer 5. These are standard EPICS records, and EPICS database (template) files
      that define records to communicate with drivers at Layer 2 and plugins at Layer
      3.</li>
    <li>Layer 6. These are EPICS channel access clients, such as MEDM that communicate
      with the records at Layer 5. areaDetector includes some client applications that
      can display images using EPICS waveform and other records communicating with the
      NDPluginStdArrays plugin at Layer 3. Several of these clients are ImageJ plugins,
      and the other is a freely runnable IDL application.</li>
  </ul>
  <p>
    The code in Layers 1-3 is essentially independent of EPICS. In principle there are
    only 2 EPICS dependencies in this code.
  </p>
  <ol>
    <li><a href="http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide/">libCom</a>.
      libCom from EPICS base provides operating-system independent functions for threads,
      mutexes, etc.</li>
    <li><a href="http://www.aps.anl.gov/epics/modules/soft/asyn/">asyn</a>. asyn is a
      module that provides interthread messaging services, including queueing and callbacks.</li>
  </ol>
  <p>
    In particular it is possible to eliminate layers 4-6 in the architecture shown in
    Figure 1. This means that it is not necessary to run an EPICS IOC or to use EPICS
    Channel Access when using the drivers and plugins at Layers 2 and 3. This is demonstrated
    in the simDetectorNoIOC application in ADSimDetector and in the unit tests in ADCore/ADApp/pluginTests.
  </p>
  <p>
    The plugin architecture is very powerful, because new plugins can be written for
    application-specific purposes. For example, a plugin could be written to analyze
    images and do some application specific functions, and such a plugin would then
    work with any detector driver. Plugins are also powerful because they can be reconfigured
    at run-time. For example the NDPluginStdArrays can switch from getting its array
    data from a detector driver to an NDPluginROI plugin. That way it will switch from
    displaying the entire detector to whatever sub-region the ROI driver has selected.
    Any Channel Access clients connected to the NDPluginStdArrays driver will automatically
    switch to displaying this subregion. Similarly, the NDPluginFile plugin can be switched
    at run-time from saving the entire image to saving a selected ROI, just by changing
    its input source. Plugins can be used to form an image processing pipeline, for
    example with a detector providing data to a color convert plugin, which feeds an
    ROI plugin, which feeds a file saving plugin. Each plugin can run in its own thread,
    and hence in its own cores on a modern multi-core CPU.
  </p>
  <p>
    The use of plugins is optional, and it is only plugins that require the driver to
    make callbacks with image data. If there are no plugins being used then EPICS can
    be used simply to control the detector, without accessing the data itself. This
    is most useful when the vendor provides an API has the ability to save the data
    to a file and an application to display the images.
  </p>
  <p>
    What follows is a detailed description of the software, working from the bottom
    up. Most of the code is object oriented, and written in C++.
  </p>
  <h2 id="Implementation_details">
    Implementation details</h2>
  <p>
    The areaDetector module depends heavily on <a href="http://www.aps.anl.gov/epics/modules/soft/asyn/">
      asyn</a>. It is the software that is used for interthread communication, using
    the standard asyn interfaces (e.g. asynInt32, asynOctet, etc.), and callbacks. In
    order to minimize the amount of redundant code in drivers, areaDetector has been
    implemented using C++ classes. The base classes, from which drivers and plugins
    are derived, take care of many of the details of asyn and other common code.
  </p>
  <h3 id="asynPortDriver">
    asynPortDriver</h3>
  <p>
    Detector drivers and plugins are asyn port drivers, meaning that they implement
    one or more of the standard asyn interfaces. They register themselves as interrupt
    sources, so that they do callbacks to registered asyn clients when values change.
    They inherit from the <a href="http://www.aps.anl.gov/epics/modules/soft/asyn/R4-32/asynPortDriver.html">
      asynPortDriver base C++ class</a> that is provided in the asyn module. That base
    class handles all of the details of registering the port driver, registering the
    supported interfaces, and registering the required interrupt sources. It also provides
    a parameter library for int, double, and string parameters indexed by the integer
    index values defined in the driver. The parameter library provides methods to write
    and read the parameter values, and to perform callbacks to registered clients when
    a parameter value has changed. The <a href="http://www.aps.anl.gov/epics/modules/soft/asyn/R4-32/asynDoxygenHTML/classasyn_port_driver.html">
      asynPortDriver class documentation</a> describes this class in detail.
  </p>
  <h3 id="NDArray">
    NDArray</h3>
  <p>
    The NDArray (N-Dimensional array) is the class that is used for passing detector
    data from drivers to plugins. An NDArray is a general purpose class for handling
    array data. An NDArray object is self-describing, meaning it contains enough information
    to describe the data itself. It can optionally contain "attributes" (class NDAttribute)
    which contain meta-data describing how the data was collected, etc.
  </p>
  <p>
    An NDArray can have up to ND_ARRAY_MAX_DIMS dimensions, currently 10. A fixed maximum
    number of dimensions is used to significantly simplify the code compared to unlimited
    number of dimensions. Each dimension of the array is described by an <a href="areaDetectorDoxygenHTML/struct_n_d_dimension.html">
      NDDimension structure</a>. The <a href="areaDetectorDoxygenHTML/class_n_d_array.html">
        NDArray class documentation </a>describes this class in detail.
  </p>
  <h3 id="NDArrayPool">
    NDArrayPool</h3>
  <p>
    The NDArrayPool class manages a free list (pool) of NDArray objects. Drivers allocate
    NDArray objects from the pool, and pass these objects to plugins. Plugins increase
    the reference count on the object when they place the object on their queue, and
    decrease the reference count when they are done processing the array. When the reference
    count reaches 0 again the NDArray object is placed back on the free list. This mechanism
    minimizes the copying of array data in plugins. The <a href="areaDetectorDoxygenHTML/class_n_d_array_pool.html">
      NDArrayPool class documentation </a>describes this class in detail.
  </p>
  <h3 id="NDAttribute">
    NDAttribute</h3>
  <p>
    The NDAttribute is a class for linking metadata to an NDArray. An NDattribute has
    a name, description, data type, value, source type and source information. Attributes
    are identified by their names, which are case-sensitive. There are methods to set
    and get the information for an attribute.</p>
  <p>
    It is useful to define some conventions for attribute names, so that plugins or
    data analysis programs can look for a specific attribute. The following are the
    attribute conventions used in current plugins:</p>
  <table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
    <tbody>
      <tr>
        <td align="center" colspan="4">
          <b>Conventions for standard attribute names</b>
        </td>
      </tr>
      <tr>
        <th>
          Attribute name
        </th>
        <th>
          Description
        </th>
        <th>
          Data type
        </th>
      </tr>
      <tr>
        <td>
          ColorMode
        </td>
        <td>
          Color mode of the NDArray. Used by many plugins to determine how to process the
          array.
        </td>
        <td>
          NDAttrInt32 (NDColorMode_t)
        </td>
      </tr>
      <tr>
        <td>
          BayerPattern
        </td>
        <td>
          Bayer patter of an image collect by a color camera with a Bayer mask. Could be used
          to convert to a an RGB color image. This capability may be added to NDPluginColorConvert.
        </td>
        <td>
          NDAttrInt32 (NDBayerPattern_t)
        </td>
      </tr>
      <tr>
        <td>
          DriverFilename
        </td>
        <td>
          The name of the file originally collected by the driver. This is used by NDPluginFile
          to delete the original driver file if the DeleteDriverFile flag is set and the NDArray
          has been successfully saved in another file.
        </td>
        <td>
          NDAttrString
        </td>
      </tr>
      <tr>
        <td>
          FilePluginDestination
        </td>
        <td>
          This is used by NDPluginFile to determine whether to process this NDArray. If this
          attribute is present and is "all" or the name of this plugin then the NDArray is
          processed, otherwise it is ignored.
        </td>
        <td>
          NDAttrString
        </td>
      </tr>
      <tr>
        <td>
          FilePluginFileName
        </td>
        <td>
          This is used by NDPluginFile to set the file name when saving this NDArray.
        </td>
        <td>
          NDAttrString
        </td>
      </tr>
      <tr>
        <td>
          FilePluginFileNumber
        </td>
        <td>
          This is used by NDPluginFile to set the file number when saving this NDArray.
        </td>
        <td>
          NDAttrInt32
        </td>
      </tr>
      <tr>
        <td>
          FilePluginFileClose
        </td>
        <td>
          This is used by NDPluginFile to close the file after processing this NDArray.
        </td>
        <td>
          NDAttrInt32
        </td>
      </tr>
      <tr>
        <td>
          [HDF dataset name]
        </td>
        <td>
          This is used by NDFileHDF5 to determine which dataset in the file this NDArray should
          be written to. The attribute name is the name of the HDF5 dataset.
        </td>
        <td>
          NDAttrString
        </td>
      </tr>
      <tr>
        <td>
          [posName]
        </td>
        <td>
          This is used by NDFileHDF5 to determine which position in the dataset this NDArray
          should be written to. The attribute name is contained in a *PosName* record defined
          in NDFileHDF5.template. This is designed to allow, for example, "snake scan" data
          to be placed in the correct order in an HDF5 file.
        </td>
        <td>
          NDAttrInt32
        </td>
      </tr>
    </tbody>
  </table>
  <p>
    Attribute names are case-sensitive. For attributes not in this table a good convention
    would be to use the corresponding driver parameter without the leading ND or AD,
    and with the first character of every "word" of the name starting with upper case.
    For example, the standard attribute name for ADManufacturer should be "Manufacturer",
    ADNumExposures should be "NumExposures", etc.</p>
  <p>
    The <a href="areaDetectorDoxygenHTML/class_n_d_attribute.html">NDAttribute class documentation</a>
    describes this class in detail.
  </p>
  <h3 id="NDAttributeList">
    NDAttributeList</h3>
  <p>
    The NDAttributeList implements a linked list of NDAttribute objects. NDArray objects
    contain an NDAttributeList which is how attributes are associated with an NDArray.
    There are methods to add, delete and search for NDAttribute objects in an NDAttributeList.
    Each attribute in the list must have a unique name, which is case-sensitive.
  </p>
  <p>
    When NDArrays are copied with the NDArrayPool methods the attribute list is also
    copied.
  </p>
  <p>
    IMPORTANT NOTE: When a new NDArray is allocated using NDArrayPool::alloc() the behavior
    of any existing attribute list on the NDArray taken from the pool is determined
    by the value of the global variable <code>eraseNDAttributes</code>. By default the
    value of this variable is 0. This means that when a new NDArray is allocated from
    the pool its attribute list is <strong>not</strong> cleared. This greatly improves
    efficiency in the normal case where attributes for a given driver are defined once
    at initialization and never deleted. (The attribute <strong>values</strong> may
    of course be changing.) It eliminates allocating and deallocating attribute memory
    each time an array is obtained from the pool. It is still possible to add new attributes
    to the array, but any existing attributes will continue to exist even if they are
    ostensibly cleared e.g. asynNDArrayDriver::readNDAttributesFile() is called again.
    If it is desired to eliminate all existing attributes from NDArrays each time a
    new one is allocated then the global variable <code>eraseNDAttributes</code> should
    be set to 1. This can be done at the iocsh prompt with the command:</p>
  <pre>    var eraseNDAttributes 1
    </pre>
  <p>
    The <a href="areaDetectorDoxygenHTML/class_n_d_attribute_list.html">NDAttributeList
      class documentation</a> describes this class in detail.
  </p>
  <h3 id="PVAttribute">
    PVAttribute</h3>
  <p>
    The PVAttribute class is derived from NDAttribute. It obtains its value by monitor
    callbacks from an EPICS PV, and is thus used to associate current the value of any
    EPICS PV with an NDArray. The <a href="areaDetectorDoxygenHTML/class_p_v_attribute.html">
      PVAttribute class documentation</a> describes this class in detail.
  </p>
  <h3 id="paramAttribute">
    paramAttribute</h3>
  <p>
    The paramAttribute class is derived from NDAttribute. It obtains its value from
    the current value of a driver or plugin parameter. The paramAttribute class is typically
    used when it is important to have the current value of the parameter and the value
    of a corresponding PVAttribute might not be current because the EPICS PV has not
    yet updated. The <a href="areaDetectorDoxygenHTML/classparam_attribute.html">paramAttribute
      class documentation</a> describes this class in detail.
  </p>
  <h3 id="functAttribute">
    functAttribute</h3>
  <p>
    The functAttribute class is derived from NDAttribute. It obtains its value from
    a user-written C++ function. The functAttribute class is thus very general, and
    can be used to add almost any information to an NDArray. ADCore contains example
    code, myAttributeFunctions.cpp that demonstates how to write such functions. The
    <a href="areaDetectorDoxygenHTML/classfunct_attribute.html">functAttribute class documentation</a>
    describes this class in detail.
  </p>
  <h3 id="asynNDArrayDriver">
    asynNDArrayDriver</h3>
  <p>
    asynNDArrayDriver inherits from asynPortDriver. It implements the asynGenericPointer
    functions for NDArray objects. This is the class from which both plugins and area
    detector drivers are indirectly derived. The <a href="areaDetectorDoxygenHTML/classasyn_n_d_array_driver.html">
      asynNDArrayDriver class documentation </a>describes this class in detail.
  </p>
  <p>
    The file <a href="areaDetectorDoxygenHTML/asyn_n_d_array_driver_8h.html">asynNDArrayDriver.h</a>
    defines a number of parameters that all NDArray drivers and plugins should implement
    if possible. These parameters are defined by strings (drvInfo strings in asyn) with
    an associated asyn interface, and access (read-only or read-write). There is also
    an integer index to the parameter which is assigned by asynPortDriver when the parameter
    is created in the parameter library. The EPICS database NDArrayBase.template provides
    access to these standard driver parameters. The following table lists the standard
    driver parameters. The columns are defined as follows:
  </p>
  <ul>
    <li><b>Parameter index variable:</b> The variable name for this parameter index in
      the driver. There are several EPICS records in ADBase.template that do not have
      corresponding parameter indices, and these are indicated as Not Applicable (N/A).</li>
    <li><b>asyn interface:</b> The asyn interface used to pass this parameter to the driver.</li>
    <li><b>Access:</b> Read-write (r/w) or read-only (r/o).</li>
    <li><b>drvInfo string:</b> The string used to look up the parameter in the driver
      through the drvUser interface. This string is used in the EPICS database file for
      generic asyn device support to associate a record with a particular parameter. It
      is also used to associate a <a href="areaDetectorDoxygenHTML/classparam_attribute.html">
        paramAttribute</a> with a driver parameter in the XML file that is read by asynNDArrayDriver::readNDAttributesFile</li>
    <li><b>EPICS record name:</b> The name of the record in ADBase.template. Each record
      name begins with the two macro parameters $(P) and $(R). In the case of read/write
      parameters there are normally two records, one for writing the value, and a second,
      ending in _RBV, that contains the actual value (Read Back Value) of the parameter.</li>
    <li><b>EPICS record type:</b> The record type of the record. Waveform records are
      used to hold long strings, with length (NELM) = 256 bytes and EPICS data type (FTVL)
      = UCHAR. This removes the 40 character restriction string lengths that arise if
      an EPICS "string" PV is used. MEDM allows one to edit and display such records correctly.
      EPICS clients will typically need to convert such long strings from a string to
      an integer or byte array before sending the path name to EPICS. In IDL this is done
      as follows:
      <pre>      ; Convert a string to a null-terminated byte array and write with caput
      IDL> t = caput('13PS1:TIFF1:FilePath', [byte('/home/epics/scratch'),0B])
      ; Read a null terminated byte array 
      IDL> t = caget('13PS1:TIFF1:FilePath', v)
      ; Convert to a string 
      IDL> s = string(v) 
      </pre>
      In SPEC this is done as follows:
      <pre>      array _temp[256]
      # Setting the array to "" will zero-fill it
      _temp = ""
      # Copy the string to the array.  Note, this does not null terminate, so if array already contains
      # a longer string it needs to first be zeroed by setting it to "".
      _temp = "/home/epics/scratch"
      epics_put("13PS1:TIFF1:FilePath", _temp)
      </pre>
    </li>
  </ul>
  <p>
    Note that for parameters whose values are defined by enum values (e.g NDDataType,
    NDColorMode, etc.), drivers can use a different set of enum values for these parameters.
    They can override the enum menu in ADBase.template with driver-specific choices
    by loading a driver-specific template file that redefines that record field after
    loading ADBase.template.
  </p>
  <table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
    <tbody>
      <tr>
        <td align="center" colspan="7">
          <b>Parameter Definitions in asynNDArrayDriver.h and EPICS Record Definitions in NDArrayBase.template
            (file-related records are in NDFile.template)</b>
        </td>
      </tr>
      <tr>
        <th>
          Parameter index variable
        </th>
        <th>
          asyn interface
        </th>
        <th>
          Access
        </th>
        <th>
          Description
        </th>
        <th>
          drvInfo string
        </th>
        <th>
          EPICS record name
        </th>
        <th>
          EPICS record type
        </th>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Information about the version of ADCore and the plugin or driver</b>
        </td>
      </tr>
      <tr>
        <td>
          NDADCoreVersion
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          ADCore version number. This can be used by Channel Access clients to alter their
          behavior depending on the version of ADCore that was used to build this driver or
          plugin.
        </td>
        <td>
          ADCORE_VERSION
        </td>
        <td>
          $(P)$(R)ADCoreVersion_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          NDDriverVersion
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Driver or plugin version number. This can be used by Channel Access clients to alter
          their behavior depending on the version of the plugin or driver.
        </td>
        <td>
          DRIVER_VERSION
        </td>
        <td>
          $(P)$(R)DriverVersion_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Information about the asyn port</b>
        </td>
      </tr>
      <tr>
        <td>
          NDPortNameSelf
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          asyn port name
        </td>
        <td>
          PORT_NAME_SELF
        </td>
        <td>
          $(P)$(R)PortName_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Data type</b>
        </td>
      </tr>
      <tr>
        <td>
          NDDataType
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Data type (NDDataType_t).
        </td>
        <td>
          DATA_TYPE
        </td>
        <td>
          $(P)$(R)DataType<br />
          $(P)$(R)DataType_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Color mode</b>
        </td>
      </tr>
      <tr>
        <td>
          NDColorMode
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Color mode (NDColorMode_t).
        </td>
        <td>
          COLOR_MODE
        </td>
        <td>
          $(P)$(R)ColorMode<br />
          $(P)$(R)ColorMode_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          NDBayerPattern
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Bayer pattern (NDBayerPattern_t) of NDArray data.
        </td>
        <td>
          BAYER_PATTERN
        </td>
        <td>
          $(P)$(R)BayerPattern_RBV
        </td>
        <td>
          mbbi
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Actual dimensions of array data</b>
        </td>
      </tr>
      <tr>
        <td>
          NDNDimensions
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Number of dimensions in the array
        </td>
        <td>
          ARRAY_NDIMENSIONS
        </td>
        <td>
          $(P)$(R)NDimensions<br />
          $(P)$(R)NDimensions_RBV
        </td>
        <td>
          longout
          <br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDDimensions
        </td>
        <td>
          asynInt32Array
        </td>
        <td>
          r/w
        </td>
        <td>
          Size of each dimension in the array
        </td>
        <td>
          ARRAY_DIMENSIONS
        </td>
        <td>
          $(P)$(R)Dimensions<br />
          $(P)$(R)Dimensions_RBV
        </td>
        <td>
          waveform (out)<br />
          waveform (in)
        </td>
      </tr>
      <tr>
        <td>
          N.A.
        </td>
        <td>
          N.A
        </td>
        <td>
          r/o
        </td>
        <td>
          Size of each array dimension, extracted from the $(P)$(R)Dimensions and $(P)$(R)Dimensions_RBV
          waveform records. Note that these are both longin record, i.e. readonly values using
          subarray records. In the future longout records may be added to write to the individual
          values in $(P)$(R)Dimensions.
        </td>
        <td>
          N.A.
        </td>
        <td>
          $(P)$(R)ArraySize[N] N=0-9
          <br />
          (P)$(R)ArraySize[N]_RBV
        </td>
        <td>
          longin
          <br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDArraySizeX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Size of the array data in the X direction
        </td>
        <td>
          ARRAY_SIZE_X
        </td>
        <td>
          $(P)$(R)ArraySizeX_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDArraySizeY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Size of the array data in the Y direction
        </td>
        <td>
          ARRAY_SIZE_Y
        </td>
        <td>
          $(P)$(R)ArraySizeY_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDArraySizeZ
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Size of the array data in the Z direction
        </td>
        <td>
          ARRAY_SIZE_Z
        </td>
        <td>
          $(P)$(R)ArraySizeZ_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDArraySize
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Total size of the array data in bytes
        </td>
        <td>
          ARRAY_SIZE
        </td>
        <td>
          $(P)$(R)ArraySize_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Array data</b>
        </td>
      </tr>
      <tr>
        <td>
          NDArrayCallbacks
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Controls whether the driver or plugin does callbacks with the array data to registered
          plugins. 0=No, 1=Yes. Setting this to 0 in a driver can reduce overhead in the case
          that the driver is being used only to control the device, and not to make the data
          available to plugins or to EPICS clients. Setting this to 0 in a plugin can reduce
          overhead by eliminating the need to copy the NDArray if that plugin is not being
          used as a source of NDArrays to other plugins.
        </td>
        <td>
          ARRAY_CALLBACKS
        </td>
        <td>
          $(P)$(R)ArrayCallbacks<br />
          $(P)$(R)ArrayCallbacks_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDArrayData
        </td>
        <td>
          asynGenericPointer
        </td>
        <td>
          r/w
        </td>
        <td>
          The array data as an NDArray object
        </td>
        <td>
          NDARRAY_DATA
        </td>
        <td>
          N/A. EPICS access to array data is through NDStdArrays plugin.
        </td>
        <td>
          N/A
        </td>
      </tr>
      <tr>
        <td>
          NDArrayCounter
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Counter that increments by 1 each time an array is acquired. Can be reset by writing
          a value to it.
        </td>
        <td>
          ARRAY_COUNTER
        </td>
        <td>
          $(P)$(R)ArrayCounter<br />
          $(P)$(R)ArrayCounter_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          r/o
        </td>
        <td>
          Rate at which arrays are being acquired. Computed in the ADBase.template database.
        </td>
        <td>
          N/A
        </td>
        <td>
          $(P)$(R)ArrayRate_RBV
        </td>
        <td>
          calc
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Array attributes</b>
        </td>
      </tr>
      <tr>
        <td>
          NDAttributesFile
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          The name of an XML file defining the NDAttributes to be added to each NDArray by
          this driver or plugin. The format of the XML file is described in the documentation
          for <a href="areaDetectorDoxygenHTML/classasyn_n_d_array_driver.html">asynNDArrayDriver::readNDAttributesFile().</a>
        </td>
        <td>
          ND_ATTRIBUTES_FILE
        </td>
        <td>
          $(P)$(R)NDAttributesFile
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDAttributesMacros
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          A macro definition string that can be used to do macro substitution in the XML file.
          For example if this string is "CAMERA=13SIM1:cam1:,ID=ID13us:" then all $(CAMERA)
          and $(ID) strings in the XML file will be replaced with 13SIM1:cam1: and ID13us:
          respectively.
        </td>
        <td>
          ND_ATTRIBUTES_MACROS
        </td>
        <td>
          $(P)$(R)NDAttributesMacros
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDAttributesStatus
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          The status of reading and parsing the XML attributes file. This is used to indicate
          if the file cannot be found, if there is an XML syntax error, or if there is a macro
          substitutions error.
        </td>
        <td>
          ND_ATTRIBUTES_STATUS
        </td>
        <td>
          $(P)$(R)NDAttributesStatus
        </td>
        <td>
          mbbi
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Array pool status</b>
        </td>
      </tr>
      <tr>
        <td>
          NDPoolMaxMemory
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/o
        </td>
        <td>
          The maximum number of NDArrayPool memory bytes that can be allocated. 0=unlimited.
        </td>
        <td>
          POOL_MAX_MEMORY
        </td>
        <td>
          $(P)$(R)PoolMaxMem
        </td>
        <td>
          ai
        </td>
      </tr>
      <tr>
        <td>
          NDPoolUsedMemory
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/o
        </td>
        <td>
          The number of NDArrayPool memory bytes currently allocated. The SCAN rate of this
          record controls the scanning of all of the dynamic NDArrayPool status records.
        </td>
        <td>
          POOL_USED_MEMORY
        </td>
        <td>
          $(P)$(R)PoolUsedMem
        </td>
        <td>
          ai
        </td>
      </tr>
      <tr>
        <td>
          NDPoolAllocBuffers
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          The number of NDArrayPool buffers currently allocated.
        </td>
        <td>
          POOL_ALLOC_BUFFERS
        </td>
        <td>
          $(P)$(R)PoolAllocBuffers
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDPoolFreeBuffers
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          The number of NDArrayPool buffers currently allocated but free.
        </td>
        <td>
          POOL_FREE_BUFFERS
        </td>
        <td>
          $(P)$(R)PoolFreeBuffers
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          N.A.
        </td>
        <td>
          N.A.
        </td>
        <td>
          r/o
        </td>
        <td>
          The number of NDArrayPool buffers currently in use. This is calculated as NDPoolAllocBuffers
          - NDPoolFreeBuffers.
        </td>
        <td>
          N.A.
        </td>
        <td>
          $(P)$(R)PoolUsedBuffers
        </td>
        <td>
          calc
        </td>
      </tr>
      <tr>
        <td>
          NDPoolEmptyFreeList
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Processing this record deletes all of the NDArrays on the freelist and sets the
          freelist size to 0. This provides a mechanism to free large amounts of memory and
          return it to the operating system, for example after a rapid acquisition with large
          plugin queues. On Windows the memory is returned to the operating system immediately.
          On Linux the freed memory may not actually be returned to the operating system even
          though it has been freed in the areaDetector process. On Centos7 (and presumably
          many other versions of Linux) setting the value of the environment variable MALLOC_TRIM_THRESHOLD_
          to a small value will allow the memory to actually be returned to the operating
          system.
        </td>
        <td>
          POOL_EMPTY_FREELIST
        </td>
        <td>
          $(P)$(R)EmptyFreeList
        </td>
        <td>
          bo
        </td>
      </tr>
      <tr>
        <td>
          NDNumQueuedArrays
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          The number of NDArrays from this driver's NDArrayPool that are currently queued
          for processing by plugins. When this number goes to 0 the plugins have all completed
          processing.
        </td>
        <td>
          NUM_QUEUED_ARRAYS
        </td>
        <td>
          $(P)$(R)NumQueuedArrays
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Debugging control</b>
        </td>
      </tr>
      <tr>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          asyn record to control debugging (asynTrace)
        </td>
        <td>
          N/A
        </td>
        <td>
          $(P)$(R)AsynIO
        </td>
        <td>
          asyn
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>File saving parameters (records are defined in NDFile.template)</b>
        </td>
      </tr>
      <tr>
        <td>
          NDFilePath
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          File path
        </td>
        <td>
          FILE_PATH
        </td>
        <td>
          $(P)$(R)FilePath<br />
          $(P)$(R)FilePath_RBV
        </td>
        <td>
          waveform<br />
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDFilePathExists
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Flag indicating if file path exists
        </td>
        <td>
          FILE_PATH_EXISTS
        </td>
        <td>
          $(P)$(R)FilePathExists_RBV
        </td>
        <td>
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileName
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          File name
        </td>
        <td>
          FILE_NAME
        </td>
        <td>
          $(P)$(R)FileName<br />
          $(P)$(R)FileName_RBV
        </td>
        <td>
          waveform<br />
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDFileNumber
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          File number
        </td>
        <td>
          FILE_NUMBER
        </td>
        <td>
          $(P)$(R)FileNumber<br />
          $(P)$(R)FileNumber_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDFileTemplate
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          Format string for constructing NDFullFileName from NDFilePath, NDFileName, and NDFileNumber.
          The final file name (which is placed in NDFullFileName) is created with the following
          code:
          <pre>epicsSnprintf(
    FullFilename, 
    sizeof(FullFilename), 
    FileTemplate, FilePath, 
    Filename, FileNumber);
        </pre>
          FilePath, Filename, FileNumber are converted in that order with FileTemplate. An
          example file format is <code>"%s%s%4.4d.tif"</code>. The first %s converts the FilePath,
          followed immediately by another %s for Filename. FileNumber is formatted with %4.4d,
          which results in a fixed field with of 4 digits, with leading zeros as required.
          Finally, the .tif extension is added to the file name. This mechanism for creating
          file names is very flexible. Other characters, such as _ can be put in Filename
          or FileTemplate as desired. If one does not want to have FileNumber in the file
          name at all, then just omit the %d format specifier from FileTemplate. If the client
          wishes to construct the complete file name itself, then it can just put that file
          name into NDFileTemplate with no format specifiers at all, in which case NDFilePath,
          NDFileName, and NDFileNumber will be ignored.
        </td>
        <td>
          FILE_TEMPLATE
        </td>
        <td>
          $(P)$(R)FileTemplate<br />
          $(P)$(R)FileTemplate_RBV
        </td>
        <td>
          waveform<br />
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDFullFileName
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Full file name constructed using the algorithm described in NDFileTemplate
        </td>
        <td>
          FULL_FILE_NAME
        </td>
        <td>
          $(P)$(R)FullFileName_RBV
        </td>
        <td>
          waveform<br />
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDAutoIncrement
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Auto-increment flag. Controls whether FileNumber is automatically incremented by
          1 each time a file is saved (0=No, 1=Yes)
        </td>
        <td>
          AUTO_INCREMENT
        </td>
        <td>
          $(P)$(R)AutoIncrement<br />
          $(P)$(R)AutoIncrement_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDAutoSave
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Auto-save flag (0=No, 1=Yes) controlling whether a file is automatically saved each
          time acquisition completes.
        </td>
        <td>
          AUTO_SAVE
        </td>
        <td>
          $(P)$(R)AutoSave<br />
          $(P)$(R)AutoSave_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileFormat
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          File format. The format to write/read data in (e.g. TIFF, netCDF, etc.)
        </td>
        <td>
          FILE_FORMAT
        </td>
        <td>
          $(P)$(R)FileFormat<br />
          $(P)$(R)FileFormat_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          NDWriteFile
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Manually save the most recent array to a file when value=1
        </td>
        <td>
          WRITE_FILE
        </td>
        <td>
          $(P)$(R)WriteFile<br />
          $(P)$(R)WriteFile_RBV
        </td>
        <td>
          busy<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDReadFile
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Manually read a file when value=1
        </td>
        <td>
          READ_FILE
        </td>
        <td>
          $(P)$(R)ReadFile<br />
          $(P)$(R)ReadFile_RBV
        </td>
        <td>
          busy<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileWriteMode
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          File saving mode (Single, Capture, Stream)(NDFileMode_t)
        </td>
        <td>
          WRITE_MODE
        </td>
        <td>
          $(P)$(R)FileWriteMode<br />
          $(P)$(R)FileWriteMode_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          NDFileWriteStatus
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          File write status. Gives status information on last file open or file write operation.
          Values are WriteOK (0) and WriteError (1).
        </td>
        <td>
          WRITE_STATUS
        </td>
        <td>
          $(P)$(R)FileWriteStatus
        </td>
        <td>
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          NDFileWriteMessage
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          File write error message. An error message string if the previous file open or file
          write operation resulted in an error.
        </td>
        <td>
          WRITE_MESSAGE
        </td>
        <td>
          $(P)$(R)FileWriteMessage
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          NDFileCapture
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Start (1) or stop (0) file capture or streaming
        </td>
        <td>
          CAPTURE
        </td>
        <td>
          $(P)$(R)Capture<br />
          $(P)$(R)Capture_RBV
        </td>
        <td>
          busy<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileNumCapture
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Number of frames to acquire in capture or streaming mode
        </td>
        <td>
          NUM_CAPTURE
        </td>
        <td>
          $(P)$(R)NumCapture<br />
          $(P)$(R)NumCapture_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDFileNumCaptured
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Number of arrays currently acquired capture or streaming mode
        </td>
        <td>
          NUM_CAPTURED
        </td>
        <td>
          $(P)$(R)NumCaptured_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDFileDeleteDriverFile
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Flag to enable deleting original driver file after a plugin has re-written the file
          in a different format. This can be useful for detectors that must write the data
          to disk in order for the areaDetector driver to read it back. Once a file-writing
          plugin has rewritten the data in another format it can be desireable to then delete
          the original file.
        </td>
        <td>
          DELETE_DRIVER_FILE
        </td>
        <td>
          $(P)$(R)DeleteDriverFile<br />
          $(P)$(R)DeleteDriverFile_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileLazyOpen
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Flag to defer the creation of a new file until the first NDArray to write has been
          received. This removes the need for passing an extra NDArray through the file writing
          plugin to initialise dimensions and possibly NDAttribute list before opening the
          file. The downside is that file creation can potentially be time-consuming so processing
          the first NDArray may be slower than subsequent ones.
          <br />
          Only makes sense to use with file plugins which support multiple frames per file
          and only in "Stream" mode.
        </td>
        <td>
          FILE_LAZY_OPEN
        </td>
        <td>
          $(P)$(R)LazyOpen<br />
          $(P)$(R)LazyOpen_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          NDFileCreateDir
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          This parameter is used to automatically create directories if they don't exist.
          If it is zero (default), no directories are created. If it is negative, then the
          absolute value is the maximum of directories that will be created (i.e. -1 will
          create a maximum of one directory to complete the path, -2 will create a maximum
          of 2 directories). If it is positive, then at least that many directories in the
          path must exist (i.e. a value of 1 will create all directories below the root directory
          and 2 will not create a directory in the root directory).
        </td>
        <td>
          CREATE_DIR
        </td>
        <td>
          $(P)$(R)CreateDirectory<br />
          $(P)$(R)CreateDirectory_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          NDFileTempSuffix
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/w
        </td>
        <td>
          If this string is non-null, the file is opened with this suffix temporarily appended
          to the file name. When the file is closed it is then renamed to the correct file
          name without the suffix. This is useful for processing software watching for the
          file to appear since the file appears as an atomic operation when it is ready to
          be opened.
        </td>
        <td>
          FILE_TEMP_SUFFIX
        </td>
        <td>
          $(P)$(R)TempSuffix<br />
          $(P)$(R)TempSuffix_RBV
        </td>
        <td>
          stringout<br />
          stringin
        </td>
      </tr>
    </tbody>
  </table>
  <h3 id="ADDriver">
    ADDriver</h3>
  <p>
    ADDriver inherits from asynNDArrayDriver. This is the class from which area detector
    drivers are directly derived. It provides parameters and methods that are specific
    to area detectors, while asynNDArrayDriver is a general NDArray driver. The <a href="areaDetectorDoxygenHTML/class_a_d_driver.html">
      ADDriver class documentation </a>describes this class in detail.
  </p>
  <p>
    The file <a href="areaDetectorDoxygenHTML/_a_d_driver_8h.html">ADDriver.h</a> defines
    the parameters that all areaDetector drivers should implement if possible.
  </p>
  <table border="1" cellpadding="2" cellspacing="2" style="text-align: left">
    <tbody>
      <tr>
        <td align="center" colspan="7">
          <b>Parameter Definitions in ADDriver.h and EPICS Record Definitions in ADBase.template</b>
        </td>
      </tr>
      <tr>
        <th>
          Parameter index variable
        </th>
        <th>
          asyn interface
        </th>
        <th>
          Access
        </th>
        <th>
          Description
        </th>
        <th>
          drvInfo string
        </th>
        <th>
          EPICS record name
        </th>
        <th>
          EPICS record type
        </th>
      </tr>
      <tr>
        <td align="center" colspan="7" style="height: 25px">
          <b>Information about the detector</b>
        </td>
      </tr>
      <tr>
        <td class="style1">
          ADManufacturer
        </td>
        <td class="style1">
          asynOctet
        </td>
        <td class="style1">
          r/o
        </td>
        <td class="style1">
          Detector manufacturer name
        </td>
        <td class="style1">
          MANUFACTURER
        </td>
        <td class="style1">
          $(P)$(R)Manufacturer_RBV
        </td>
        <td class="style1">
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADModel
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Detector model name
        </td>
        <td>
          MODEL
        </td>
        <td>
          $(P)$(R)Model_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADSerialNumber
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Detector serial number
        </td>
        <td>
          SERIAL_NUMBER
        </td>
        <td>
          $(P)$(R)SerialNumber_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADFirmwareVersion
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Detector firmware version
        </td>
        <td>
          FIRMWARE_VERSION
        </td>
        <td>
          $(P)$(R)FirmwareVersion_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADSDKVersion
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Detector vendor's Software Development Kit (SDK) version number.
        </td>
        <td>
          SDK_VERSION
        </td>
        <td>
          $(P)$(R)SDKVersion_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADFirmwareVersion
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Detector firmeare version number.
        </td>
        <td>
          FIRMWARE_VERSION
        </td>
        <td>
          $(P)$(R)FirmwareVersion_RBV
        </td>
        <td>
          stringin
        </td>
      </tr>
      <tr>
        <td>
          ADMaxSizeX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Maximum (sensor) size in the X direction
        </td>
        <td>
          MAX_SIZE_X
        </td>
        <td>
          $(P)$(R)MaxSizeX_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADMaxSizeY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Maximum (sensor) size in the Y direction
        </td>
        <td>
          MAX_SIZE_Y
        </td>
        <td>
          $(P)$(R)MaxSizeY_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADTemperature
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Detector temperature
        </td>
        <td>
          TEMPERATURE
        </td>
        <td>
          $(P)$(R)Temperature<br />
          $(P)$(R)Temperature_RBV<br />
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
      <tr>
        <td>
          ADTemperatureActual
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/o
        </td>
        <td>
          Actual detector temperature
        </td>
        <td>
          TEMPERATURE_ACTUAL
        </td>
        <td>
          $(P)$(R)Temperature_Actual
        </td>
        <td>
          ai
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Detector readout control including gain, binning, region start and size, reversal</b>
        </td>
      </tr>
      <tr>
        <td>
          ADGain
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Detector gain
        </td>
        <td>
          GAIN
        </td>
        <td>
          $(P)$(R)Gain<br />
          $(P)$(R)Gain_RBV
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
      <tr>
        <td>
          ADBinX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Binning in the X direction
        </td>
        <td>
          BIN_X
        </td>
        <td>
          $(P)$(R)BinX<br />
          $(P)$(R)BinX_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADBinY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Binning in the Y direction
        </td>
        <td>
          BIN_Y
        </td>
        <td>
          $(P)$(R)BinY<br />
          $(P)$(R)BinY_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADMinX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          First pixel to read in the X direction.
          <br />
          0 is the first pixel on the detector.
        </td>
        <td>
          MIN_X
        </td>
        <td>
          $(P)$(R)MinX<br />
          $(P)$(R)MinX_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADMinY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          First pixel to read in the Y direction.<br />
          0 is the first pixel on the detector.
        </td>
        <td>
          MIN_Y
        </td>
        <td>
          $(P)$(R)MinY<br />
          $(P)$(R)MinY_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADSizeX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Size of the region to read in the X direction
        </td>
        <td>
          SIZE_X
        </td>
        <td>
          $(P)$(R)SizeX<br />
          $(P)$(R)SizeX_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADSizeY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Size of the region to read in the Y direction
        </td>
        <td>
          SIZE_Y
        </td>
        <td>
          $(P)$(R)SizeY<br />
          $(P)$(R)SizeY_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADReverseX
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Reverse array in the X direction<br />
          (0=No, 1=Yes)
        </td>
        <td>
          REVERSE_X
        </td>
        <td>
          $(P)$(R)ReverseX<br />
          $(P)$(R)ReverseX_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADReverseY
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Reverse array in the Y direction<br />
          (0=No, 1=Yes)
        </td>
        <td>
          REVERSE_Y
        </td>
        <td>
          $(P)$(R)ReverseY<br />
          $(P)$(R)ReverseY_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Image and trigger modes</b>
        </td>
      </tr>
      <tr>
        <td>
          ADImageMode
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Image mode (ADImageMode_t).
        </td>
        <td>
          IMAGE_MODE
        </td>
        <td>
          $(P)$(R)ImageMode<br />
          $(P)$(R)ImageMode_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          ADTriggerMode
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Trigger mode (ADTriggerMode_t).
        </td>
        <td>
          TRIGGER_MODE
        </td>
        <td>
          $(P)$(R)TriggerMode<br />
          $(P)$(R)TriggerMode_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Frame type</b>
        </td>
      </tr>
      <tr>
        <td>
          ADFrameType
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Frame type (ADFrameType_t).
        </td>
        <td>
          FRAME_TYPE
        </td>
        <td>
          $(P)$(R)FrameType<br />
          $(P)$(R)FrameType_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Acquisition time and period</b>
        </td>
      </tr>
      <tr>
        <td>
          ADAcquireTime
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Acquisition time per image
        </td>
        <td>
          ACQ_TIME
        </td>
        <td>
          $(P)$(R)AcquireTime<br />
          $(P)$(R)AcquireTime_RBV
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
      <tr>
        <td>
          ADAcquirePeriod
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Acquisition period between images
        </td>
        <td>
          ACQ_PERIOD
        </td>
        <td>
          $(P)$(R)AcquirePeriod<br />
          $(P)$(R)AcquirePeriod_RBV
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Number of exposures and number of images</b>
        </td>
      </tr>
      <tr>
        <td>
          ADNumExposures
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Number of exposures per image to acquire
        </td>
        <td>
          NEXPOSURES
        </td>
        <td>
          $(P)$(R)NumExposures<br />
          $(P)$(R)NumExposures_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADNumImages
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Number of images to acquire in one acquisition sequence
        </td>
        <td>
          NIMAGES
        </td>
        <td>
          $(P)$(R)NumImages<br />
          $(P)$(R)NumImages_RBV
        </td>
        <td>
          longout<br />
          longin
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Acquisition control</b>
        </td>
      </tr>
      <tr>
        <td>
          ADAcquire
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Start (1) or stop (0) image acquisition. This record forward links to $(P)$(R)AcquireBusy
          which is an EPICS busy record that does not process its forward link until acquisition
          is complete. Clients should write 1 to the Acquire record to start acquisition,
          and wait for AcquireBusy to go to 0 to know that acquisition is complete. This can
          be done automatically with ca_put_callback.
        </td>
        <td>
          ACQUIRE
        </td>
        <td>
          $(P)$(R)Acquire<br />
          $(P)$(R)Acquire_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          N.A.
        </td>
        <td>
          N.A.
        </td>
        <td>
          r/o
        </td>
        <td>
          This is an EPICS busy record that is set to 1 when Acquire is set to 1 and not process
          its forward link until acquisition is complete.
        </td>
        <td>
          N.A.
        </td>
        <td>
          $(P)$(R)AcquireBusy
        </td>
        <td>
          busy
        </td>
      </tr>
      <tr>
        <td>
          N.A.
        </td>
        <td>
          N.A.
        </td>
        <td>
          r/o
        </td>
        <td>
          This record controls whether AcquireBusy goes to 0 when the detector is done (Acquire=0),
          or whether it waits until $(P)$(R)NumQueuedArrays also goes to 0, i.e. that all
          plugins are also done. Choices are No (0) and Yes(1).
        </td>
        <td>
          N.A.
        </td>
        <td>
          $(P)$(R)WaitForPlugins
        </td>
        <td>
          bo
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Status information</b>
        </td>
      </tr>
      <tr>
        <td>
          ADStatus
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Acquisition status (ADStatus_t)
        </td>
        <td>
          STATUS
        </td>
        <td>
          $(P)$(R)DetectorState_RBV
        </td>
        <td>
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          ADStatusMessage
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          Status message string
        </td>
        <td>
          STATUS_MESSAGE
        </td>
        <td>
          $(P)$(R)StatusMessage_RBV
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          ADStringToServer
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          String from driver to string-based vendor server
        </td>
        <td>
          STRING_TO_SERVER
        </td>
        <td>
          $(P)$(R)StringToServer_RBV
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          ADStringFromServer
        </td>
        <td>
          asynOctet
        </td>
        <td>
          r/o
        </td>
        <td>
          String from string-based vendor server to driver
        </td>
        <td>
          STRING_FROM_SERVER
        </td>
        <td>
          $(P)$(R)StringFromServer_RBV
        </td>
        <td>
          waveform
        </td>
      </tr>
      <tr>
        <td>
          ADNumExposuresCounter
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Counter that increments by 1 each time an exposure is acquired for the current image.
          Driver resets to 0 when acquisition is started.
        </td>
        <td>
          NUM_EXPOSURES_COUNTER
        </td>
        <td>
          $(P)$(R)NumExposuresCounter_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADNumImagesCounter
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Counter that increments by 1 each time an image is acquired in the current acquisition
          sequence. Driver resets to 0 when acquisition is started. Drivers can use this as
          the loop counter when ADImageMode=ADImageMultiple.
        </td>
        <td>
          NUM_IMAGES_COUNTER
        </td>
        <td>
          $(P)$(R)NumImagesCounter_RBV
        </td>
        <td>
          longin
        </td>
      </tr>
      <tr>
        <td>
          ADTimeRemaining
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/o
        </td>
        <td>
          Time remaining for current image. Drivers should update this value if they are doing
          the exposure timing internally, rather than in the detector hardware.
        </td>
        <td>
          TIME_REMAINING
        </td>
        <td>
          $(P)$(R)TimeRemaining_RBV
        </td>
        <td>
          ai
        </td>
      </tr>
      <tr>
        <td>
          ADReadStatus
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Write a 1 to this parameter to force a read of the detector status. Detector drivers
          normally read the status as required, so this is usually not necessary, but there
          may be some circumstances under which forcing a status read may be needed.
        </td>
        <td>
          READ_STATUS
        </td>
        <td>
          $(P)$(R)ReadStatus
        </td>
        <td>
          bo
        </td>
      </tr>
      <tr>
        <td align="center" colspan="7">
          <b>Shutter control</b>
        </td>
      </tr>
      <tr>
        <td>
          ADShutterMode
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Shutter mode (None, detector-controlled or EPICS-controlled) (ADShutterMode_t)
        </td>
        <td>
          SHUTTER_MODE
        </td>
        <td>
          $(P)$(R)ShutterMode<br />
          $(P)$(R)ShutterMode_RBV
        </td>
        <td>
          mbbo<br />
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          ADShutterControl
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          Shutter control for the selected (detector or EPICS) shutter (ADShutterStatus_t)
        </td>
        <td>
          SHUTTER_CONTROL
        </td>
        <td>
          $(P)$(R)ShutterControl<br />
          $(P)$(R)ShutterControl_RBV
        </td>
        <td>
          bo<br />
          bi
        </td>
      </tr>
      <tr>
        <td>
          ADShutterControlEPICS
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/w
        </td>
        <td>
          This record processes when it receives a callback from the driver to open or close
          the EPICS shutter. It triggers the records below to actually open or close the EPICS
          shutter.
        </td>
        <td>
          SHUTTER_CONTROL_EPICS
        </td>
        <td>
          $(P)$(R)ShutterControlEPICS
        </td>
        <td>
          bi
        </td>
      </tr>
      <tr>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          r/w
        </td>
        <td>
          This record writes its OVAL field to its OUT field when the EPICS shutter is told
          to open. The OCAL (and hence OVAL) and OUT fields are user-configurable, so any
          EPICS-controllable shutter can be used.
        </td>
        <td>
          N/A
        </td>
        <td>
          $(P)$(R)ShutterOpenEPICS
        </td>
        <td>
          calcout
        </td>
      </tr>
      <tr>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          r/w
        </td>
        <td>
          This record writes its OVAL field to its OUT field when the EPICS shutter is told
          to close. The OCAL (and hence OVAL) and OUT fields are user-configurable, so any
          EPICS-controllable shutter can be used.
        </td>
        <td>
          N/A
        </td>
        <td>
          $(P)$(R)ShutterCloseEPICS
        </td>
        <td>
          calcout
        </td>
      </tr>
      <tr>
        <td>
          ADShutterStatus
        </td>
        <td>
          asynInt32
        </td>
        <td>
          r/o
        </td>
        <td>
          Status of the detector-controlled shutter (ADShutterStatus_t)
        </td>
        <td>
          SHUTTER_STATUS
        </td>
        <td>
          $(P)$(R)ShutterStatus_RBV
        </td>
        <td>
          bi
        </td>
      </tr>
      <tr>
        <td>
          N/A
        </td>
        <td>
          N/A
        </td>
        <td>
          r/o
        </td>
        <td>
          Status of the EPICS-controlled shutter. This record should have its input link (INP)
          set to a record that contains the open/close status information for the shutter.
          The link should have the "CP" attribute, so this record processes when the input
          changes. The ZRVL field should be set to the value of the input link when the shutter
          is closed, and the ONVL field should be set to the value of the input link when
          the shutter is open.
        </td>
        <td>
          N/A
        </td>
        <td>
          $(P)$(R)ShutterStatusEPICS_RBV
        </td>
        <td>
          mbbi
        </td>
      </tr>
      <tr>
        <td>
          ADShutterOpenDelay
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Time required for the shutter to actually open (ADShutterStatus_t)
        </td>
        <td>
          SHUTTER_OPEN_DELAY
        </td>
        <td>
          $(P)$(R)ShutterOpenDelay<br />
          $(P)$(R)ShutterOpenDelay_RBV
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
      <tr>
        <td>
          ADShutterCloseDelay
        </td>
        <td>
          asynFloat64
        </td>
        <td>
          r/w
        </td>
        <td>
          Time required for the shutter to actually close (ADShutterStatus_t)
        </td>
        <td>
          SHUTTER_CLOSE_DELAY
        </td>
        <td>
          $(P)$(R)ShutterCloseDelay<br />
          $(P)$(R)ShutterCloseDelay_RBV
        </td>
        <td>
          ao<br />
          ai
        </td>
      </tr>
    </tbody>
  </table>
  <h2 id="Guidelines">
    Guidelines and rules for drivers</h2>
  <p>
    The following are guidelines and rules for writing areaDetector drivers</p>
  <ul>
    <li>Drivers will generally implement one or more of the writeInt32(), writeFloat64()
      or writeOctet() functions if they need to act immediately on a new value of a parameter.
      For many parameters it is normally sufficient to simply have them written to the
      parameter library, and not to handle them in the writeXXX() functions. The parameters
      are then retrieved from the parameter library with the getIntParam(), getDoubleParam(),
      or getStringParam() function calls when they are needed.</li>
    <li>If the writeInt32(), writeFloat64() or writeOctet() functions are implemented
      they <b>must</b> call the base class function for parameters that they do not handle
      and whose parameter index value is less than the first parameter of this class,
      i.e. parameters that belong to a base class.</li>
    <li>Drivers will need to call the createParam() function in their constructor if they
      have additional parameters beyond those in the asynPortDriver or ADDriver base classes.</li>
    <li>Drivers will generally need to create a new thread in which they run the acquisition
      task. Some vendor libraries create such a thread themselves, and then the driver
      must just implement a callback function that runs in that thread (the Prosilica
      is an example of such a driver).</li>
    <li>The acquisition thread will typically monitor the acquisition process and perform
      periodic status update callbacks. The details of how to implement this will vary
      depending on the specifics of the vendor API. There are many existing detector drivers
      that can be used as examples of how to write a new driver.</li>
    <li>If the detector hardware does not support fixed-period acquisition or muliple-image
      acquisition sequence (ADNumImages parameter) then these should be emulated in the
      driver. The simDetector, marCCD and other drivers can be used as examples of how
      to do this.</li>
    <li>If NDArrayCallbacks is non-zero then drivers should do the following:
      <ul>
        <li>Call asynNDArrayDriver::getAttributes to attach any attributes defined for this
          driver to the current array.</li>
        <li>Call doCallbacksGenericPointer() so that registered clients can get the values
          of the new arrays. Drivers must release their mutex by calling this->unlock() before
          they call doCallbacksGenericPointer(), or a deadlock can occur if the plugin makes
          a call to one of the driver functions.</li>
      </ul>
    </li>
  </ul>
  <h2 id="MEDM_screens">
    MEDM screens</h2>
  <p>
    The following is the top-level MEDM screen that provides links to the screens for
    most of the detectors and plugins that areaDetector supports. This screen is useful
    for testing, and as a source for copying related-display menus to be placed in application-specific
    MEDM screens.
  </p>
  <div style="text-align: center">
    <p>
      <b>ADTop.adl</b></p>
    <img alt="ADTop.png" src="ADTop.png" /></div>
  <p>
    The following is the MEDM screen that provides access to the parameters in asynNDArrayDriver.h
    and ADDriver.h through records in ADBase.template. This is a top-level MEDM screen
    that will work with any areaDetector driver. Note however that many drivers will
    not implement all of these parameters, and there will usually be detector-specific
    parameters not shown in this screen, so detector-specific MEDM screens should generally
    be created that display the EPICS PVs for the features implemented for that detector.
  </p>
  <div style="text-align: center">
    <p>
      <b>ADBase.adl</b></p>
    <img alt="ADBase.png" src="ADBase.png" /></div>
  <p>
    The following is the MEDM screen that provides access to the file-related parameters
    in asynNDArrayDriver.h through records in NDFile.template. This screen is for use
    with detector drivers that directly implement file I/O.
  </p>
  <div style="text-align: center">
    <p>
      <b>NDFile.adl</b></p>
    <img alt="NDFile.png" src="NDFile.png" /></div>
  <p>
    The following is the MEDM screen that provides access to the EPICS shutter parameters
    in ADDriver.h through records in ADBase.template. This screen allows one to define
    the EPICS PVs to open the shutter, close the shutter, and determine the shutter
    status. The values of these PVs for open and close drive and status can also be
    defined. Note that in many cases the same PV will be used for open and close drive,
    but in some cases (e.g. APS safety shutters) different PVs are used for open and
    close.
  </p>
  <div style="text-align: center">
    <p>
      <b>ADEpicsShutter.adl</b></p>
    <img alt="ADEpicsShutter.png" src="ADEpicsShutter.png" /></div>
  <h2 id="Installation">
    Installation, configuration, and running</h2>
  <p>
    Installation instructions for release R2-0 and later can be found on <a href="https://github.com/areaDetector/areaDetector/blob/master/INSTALL_GUIDE.md">
      INSTALL_GUIDE.md</a> on Github.</p>
  <p>
    For releases prior to R2-0 the version of this document contained in the source
    code release contains the installation instructions.</p>
  <h2 id="Acknowledgments">
    Acknowledgements and licenses</h2>
  <p>
    "This software is based in part on the work of the Independent JPEG Group".</p>
</body>
</html>
